package hamsterpolice.todo.model;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.format.Time;
import android.util.Log;
import hamsterpolice.todo.data.Criteria;
import hamsterpolice.todo.data.DataAdapter;
import hamsterpolice.todo.data.DataNotFoundException;
import hamsterpolice.todo.data.DatabaseConstraintException;
import hamsterpolice.todo.data.ToDoListOpenHelper;
import hamsterpolice.todo.data.ToDoListTable;
import hamsterpolice.todo.data.UnhandledCriteriaException;

public class ToDoItemDataAdapter implements DataAdapter<ToDoItem> {
	
	private static final String TAG = "ToDoItemDataAdapter";
	private Context context = null;
	private SQLiteDatabase database = null;
	private String[] allColumns = new String[] { ToDoListTable.COLUMN_ID,
			ToDoListTable.COLUMN_USER_ID, ToDoListTable.COLUMN_CATEGORY_ID,
			ToDoListTable.COLUMN_NAME, ToDoListTable.COLUMN_DESCRIPTION,
			ToDoListTable.COLUMN_LOCATION, ToDoListTable.COLUMN_LATITUDE,
			ToDoListTable.COLUMN_LONGITUDE, ToDoListTable.COLUMN_DUE_DATE,
			ToDoListTable.COLUMN_COMPLETED };

	public ToDoItemDataAdapter(Context context) {
		this.context = context;
		this.database = ToDoListOpenHelper.getInstance(context)
				.getWritableDatabase();
	}

	@Override
	public Cursor get(Criteria<?> criteria) throws UnhandledCriteriaException {
		
		if(criteria instanceof IDCriterion){
			long id = ((IDCriterion) criteria).getValue().longValue();
			return getByID(id);
		}
		else if(criteria instanceof ToDoItemFilterCriteria){
			ToDoItemFilterCriteria filterCriteria = (ToDoItemFilterCriteria) criteria;
			return getByFilter(filterCriteria.getValue());
		}
		
		throw new UnhandledCriteriaException();
	}
	
	private Cursor getByFilter(ToDoItemFilter filter){
		Log.v(TAG, String.format("Getting ToDoItems for %s", filter.getUser()));
		
		Cursor cursor = null;
		
		cursor = database.query(ToDoListTable.TABLE_NAME, 
				allColumns, 
				ToDoListTable.COLUMN_USER_ID + " = " + filter.getUser().getID() +
				(filter.getCategory() != null ? (" AND " + ToDoListTable.COLUMN_CATEGORY_ID + " = " + filter.getCategory().getID()) : "") +
				(filter.getDueBefore() != null ? (" AND " + ToDoListTable.COLUMN_DUE_DATE + " <= " + filter.getDueBefore().toMillis(false)) : "") +
				(filter.getDueAfter() != null ? (" AND " + ToDoListTable.COLUMN_DUE_DATE + " >= " + filter.getDueAfter().toMillis(false)) : "") +
				(filter.isCompleted() == false ? (" AND " + ToDoListTable.COLUMN_COMPLETED + " = 0") : ""),		
				null, 
				null, 
				null, 
				/*ToDoListTable.COLUMN_DUE_DATE + " asc, " + */ToDoListTable.COLUMN_NAME + " asc");
		
		return cursor;
	}
	
	private Cursor getByID(long id){
		Log.v(TAG, String.format("Getting ToDoItem for id=%s", id));

		Cursor cursor = null;

		cursor = database.query(ToDoListTable.TABLE_NAME, allColumns,
				ToDoListTable.COLUMN_ID + " = " + id, null, null, null,
				null);

		return cursor;
	}

	@Override
	public long load(Cursor c, ToDoItem model) {
		if (c == null || c.isClosed())
			throw new IllegalArgumentException("Cursor is null or closed.");
		if (c.isBeforeFirst() || c.isAfterLast())
			throw new RuntimeException(
					"There is no record at the cursor's current location");
		
		long id = c.getLong(c.getColumnIndex(ToDoListTable.COLUMN_ID));

		model.setName(c.getString(c.getColumnIndex(ToDoListTable.COLUMN_NAME)));
		model.setDescription(c.getString(c.getColumnIndex(ToDoListTable.COLUMN_DESCRIPTION)));
		model.setCompleted(c.getShort(c.getColumnIndex(ToDoListTable.COLUMN_COMPLETED)) == 1 ? true : false);
		
		// Set DueDate
		int dueDateIndex = c.getColumnIndex(ToDoListTable.COLUMN_DUE_DATE);
		if(!c.isNull(dueDateIndex)){
			Time time = new Time();
			time.set(c.getLong(dueDateIndex));
			model.setDueDate(time);
		}
		else{
			model.setDueDate(null);
		}
		
		// Set Location
		int locIndex = c.getColumnIndex(ToDoListTable.COLUMN_LOCATION);
		int latIndex = c.getColumnIndex(ToDoListTable.COLUMN_LATITUDE);
		int longIndex = c.getColumnIndex(ToDoListTable.COLUMN_LONGITUDE);
		if(!c.isNull(latIndex) && !c.isNull(longIndex)){
			model.setLocation(new Location(c.getString(locIndex), c.getDouble(latIndex), c.getDouble(longIndex)));
		}
		else{
			model.setLocation(null);
		}
		
		// Set Category
		int categoryIndex = c.getColumnIndex(ToDoListTable.COLUMN_CATEGORY_ID);
		if(!c.isNull(categoryIndex)){
			try {
				model.setCategory(Category.get(context, c.getLong(categoryIndex)));
			} catch (DataNotFoundException e) {
				Log.e(TAG, String.format("Invalid category id on ToDoItem %s (%s)", model.getName(), id));
			}
		}
		else{
			model.setCategory(null);
		}
		
		// Set User
		try {
			model.setUser(User.get(context, c.getLong(c.getColumnIndex(ToDoListTable.COLUMN_USER_ID))));
		} catch (DataNotFoundException e) {
			throw new RuntimeException(String.format("Invalid user id on ToDoItem %s (%s)", model.getName(), id), e);
		}
		
		return id;
	}

	@Override
	public long insert(ToDoItem model) throws DatabaseConstraintException {
		if (!model.isNew())
			throw new IllegalArgumentException(
					"Cannot insert a model that is not new");
		Log.v(TAG, String.format("Inserting ToDoItem %s", model.getName()));
		
		ContentValues content = getContentValues(model);
		return database.insertOrThrow(ToDoListTable.TABLE_NAME, null, content);
	}

	@Override
	public boolean update(ToDoItem model) throws DatabaseConstraintException {
		if (model.isNew())
			throw new IllegalArgumentException(
					"Cannot update a model that is new");
		if (model.isDirty()) {
			Log.v(TAG, String.format("Updating ToDoItem for id=%s", model.getID()));
			ContentValues content = getContentValues(model);
			return 1 == database.update(ToDoListTable.TABLE_NAME, content,
					ToDoListTable.COLUMN_ID + " = " + model.getID(), null);			
		}
		
		return false;
	}
	
	private ContentValues getContentValues(ToDoItem model){
		ContentValues content = new ContentValues();
		if(model.getName() == null || model.getName().trim().length() == 0)
			throw new NullPointerException("Can't save ToDoItem that has a null or empty name.");
		content.put(ToDoListTable.COLUMN_NAME, model.getName());
		content.put(ToDoListTable.COLUMN_DESCRIPTION, model.getDescription());
		content.put(ToDoListTable.COLUMN_COMPLETED, model.isCompleted());
		if(model.getUser()==null)
			throw new NullPointerException("Can't save ToDoItem that has a null User.");
		content.put(ToDoListTable.COLUMN_USER_ID, model.getUser().getID());		
		
		Location location = model.getLocation();
		if(location!=null){
			content.put(ToDoListTable.COLUMN_LOCATION, location.getDescription());
			content.put(ToDoListTable.COLUMN_LATITUDE, location.getLatitude());
			content.put(ToDoListTable.COLUMN_LONGITUDE, location.getLongitude());
		}
		else{
			content.putNull(ToDoListTable.COLUMN_LOCATION);
			content.putNull(ToDoListTable.COLUMN_LATITUDE);
			content.putNull(ToDoListTable.COLUMN_LONGITUDE);
		}
		
		Time dueDate = model.getDueDate();
		if(dueDate!=null){
			content.put(ToDoListTable.COLUMN_DUE_DATE, dueDate.toMillis(false));
		}
		else{
			content.putNull(ToDoListTable.COLUMN_DUE_DATE);
		}
		
		Category category = model.getCategory();
		if(category!=null){
			content.put(ToDoListTable.COLUMN_CATEGORY_ID, category.getID());
		}
		else{
			content.putNull(ToDoListTable.COLUMN_CATEGORY_ID);
		}
		
		return content;
	}

	@Override
	public boolean delete(long id) {
		Log.v(TAG, String.format("Deleting ToDoItem for id=%s", id));

		return 1 == database.delete(ToDoListTable.TABLE_NAME, ToDoListTable.COLUMN_ID + " = " + id, null);
	}

}
